﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Drawing.Drawing2D;
using System.Drawing.Text;

namespace rePose
{
    public partial class MainForm : Form
    {
        class ProcessHolder : PictureBox
        {
            public IntPtr hWnd;
            public string title;
            public Rectangle rect;

            public ProcessHolder(IntPtr _hWnd)
            {
                hWnd = _hWnd;
                title = WinAPI.GetWindowTitle(hWnd);
                Image = WinAPI.CaptureWindow(hWnd);
            }
        }

        UserActivityHook hook;
        List<IntPtr> windows;
        List<ProcessHolder> process_holders = new List<ProcessHolder>();
        Bitmap tile = new Bitmap(10, 10);
        public MainForm()
        {
            InitializeComponent();
            for (int x = 0; x < tile.Width; x++)
                for (int y = 0; y < tile.Height; y++)
                    tile.SetPixel(x, y, x % 2 == 0 && y % 2 == 0 ? Color.Gray : Color.White);
            BackgroundImage = tile;
            hook = new UserActivityHook();
            hook.OnMouseActivity += new MouseEventHandler(hook_OnMouseActivity);
            hook.KeyDown += new KeyEventHandler(hook_KeyDown);
            cornerTimer.Interval = 500;
            cornerTimer.Tick += new EventHandler(cornerTimer_Tick);
            loadProcesses();

        }


        private void loadProcesses()
        {
            windows = WinAPI.getVisibleWindows();

            int pid = -1;
            foreach (IntPtr hw in windows)
            {
                ProcessHolder pb = new ProcessHolder(hw);
                if (pb.title != "Start")
                    process_holders.Add(pb);
                if (pb.title == "Program Manager")
                    pid = process_holders.Count - 1;
            }

            if (pid != -1 && process_holders.Count > 1)
            {
                ProcessHolder tmp = process_holders[0];
                process_holders[0] = process_holders[pid];
                process_holders[pid] = tmp;
            }
            layoutSlotsForArea(Screen.PrimaryScreen.Bounds);
        }

        void PainNumber(int i, Graphics g, Rectangle r)
        {
            PaintTextGlow(g, i.ToString(), 40, r, Brushes.Wheat);
        }

        string cutString(Graphics g, string so, int width)
        {
            Font f = new Font(SystemFonts.CaptionFont.FontFamily, 21);
            string s = "A";
            SizeF size = new SizeF(0, 0);
            while (true)
            {
                size = g.MeasureString(s, f);
                if (size.Width < (width - 160))
                    s += "A";
                else break;
            }
            if (so.Length < s.Length) return so;
            return so.Substring(0, s.Length);
        }

        void PainTitle(string title, Graphics g, Rectangle r)
        {
            Font f = new Font(SystemFonts.CaptionFont.FontFamily, 21);
            string cuttitle = cutString(g, title, r.Width);
            SizeF size = g.MeasureString(cuttitle, f);
            Rectangle nr = new Rectangle(
               (int)((r.Right - size.Width) > 0 ? r.Right - size.Width : r.Left),
               (int)((r.Bottom - size.Height) > 0 ? r.Bottom - size.Height : r.Top),
                r.Width, r.Height);
            PaintTextGlow(g, cuttitle, 21, nr, Brushes.Blue);
        }
        const int BlurAmt = 5;

        void PaintTextGlow(Graphics g, string OutStr, int fsize, Rectangle r, Brush b)
        {
            Brush br = new SolidBrush(Color.FromArgb(15, Color.White));
            g.SmoothingMode = SmoothingMode.HighQuality;
            g.InterpolationMode = InterpolationMode.HighQualityBilinear;
            g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;


            for (int x = r.X; x <= r.X + BlurAmt; x++)
            {
                for (int y = r.Y; y <= r.Y + BlurAmt; y++)
                {
                    g.DrawString(OutStr, new Font("Arial", fsize, FontStyle.Bold), br, new Point(x, y));
                }
            }
            Rectangle tr = new Rectangle(r.X + (BlurAmt / 2), r.Y + (BlurAmt / 2), r.Width, r.Height);
            g.DrawString(OutStr, new Font("Arial", fsize, FontStyle.Bold), b, tr);

        }

        private void MainForm_Paint(object sender, PaintEventArgs e)
        {
            int i = 0;
            foreach (ProcessHolder pb in process_holders)
            {
                Rectangle rect = pb.rect;
                Rectangle rect2 = pb.rect;
                rect2.Inflate(3, 3);
                e.Graphics.DrawRectangle(new Pen(Color.Black), rect2);
                Image tb = pb.Image.GetThumbnailImage(rect.Width, rect.Height, null, IntPtr.Zero);
                e.Graphics.DrawImage(tb, pb.rect);
                i++;
                if(Config.Instance().showNumbers)
                    PainNumber(i, e.Graphics, pb.rect);
                if(Config.Instance().showTitles)
                    PainTitle(pb.title, e.Graphics, pb.rect);
            }

        }

        private void MainForm_MouseClick(object sender, MouseEventArgs e)
        {
            int i = 0;
            foreach (ProcessHolder pb in process_holders)
            {
                Rectangle rect = pb.rect;
                if (rect.Contains(e.Location))
                {
                    activateWindow(pb);
                    break;

                }
                i++;
            }

        }

        private void activateWindow(ProcessHolder pb)
        {
            WinAPI.SetForegroundWindow(pb.hWnd);
            WindowState = FormWindowState.Minimized;
            Hide();
        }
   
        void hook_KeyDown(object sender, KeyEventArgs e)
        {
            if (configForm != null) // we are handling this manually
                return;
            if (Visible) // process key press in main form
                processMainKeyDown(e);
            else // proccess global key press
            {
                if (e.KeyCode == Config.Instance().shortcut && Config.Instance().shortcutEnabled)
                {
                    showRepose();
                }
            }
        }

        private void showRepose()
        {
            WindowState = FormWindowState.Maximized;
            Show();
        }
        
        private void processMainKeyDown(KeyEventArgs e)
        {
            if (e.KeyValue >= 49 && e.KeyValue <= 58 && Config.Instance().showNumbers)
            {
                int idx = e.KeyValue - 49;
                if (idx < process_holders.Count)
                    activateWindow(process_holders[idx]);
            }
            if (e.KeyCode == Keys.Escape)
            {
                WindowState = FormWindowState.Minimized;
                Hide();
            }
        }

        Rectangle cornerRTRect = new Rectangle(Screen.PrimaryScreen.Bounds.Width - 10, 0, 10, 10);
        Rectangle cornerRBRect = new Rectangle(Screen.PrimaryScreen.Bounds.Width - 10, Screen.PrimaryScreen.Bounds.Height - 10, 10, 10);
        Rectangle cornerLTRect = new Rectangle(0, 0, 10, 10);
        Rectangle cornerLBRect = new Rectangle(0, Screen.PrimaryScreen.Bounds.Height - 10, 10, 10);
        Timer cornerTimer = new Timer();

        void hook_OnMouseActivity(object sender, MouseEventArgs e)
        {
            cornerTimer.Tag = e;
            if (cornerHit(e) && !cornerTimer.Enabled)
            {
                cornerTimer.Start();
            }
        }

        void cornerTimer_Tick(object sender, EventArgs e)
        {
            if(cornerTimer.Tag != null && cornerHit((MouseEventArgs)cornerTimer.Tag)){
                showRepose();
            }
            cornerTimer.Stop();
        }

        private bool cornerHit(MouseEventArgs e)
        {
            return (Config.Instance().cornerLB && cornerLBRect.Contains(e.Location)) ||
                           (Config.Instance().cornerRB && cornerRBRect.Contains(e.Location)) ||
                           (Config.Instance().cornerRT && cornerRTRect.Contains(e.Location)) ||
                           (Config.Instance().cornerLT && cornerLTRect.Contains(e.Location));
        }

        void layoutSlotsForArea(Rectangle workArea)
        {
            int i, j;
            int x, y, width, height;
            int lines, n, nSlots;
            int spacing;

            lines = (int)Math.Sqrt(process_holders.Count + 1);
            spacing = 35; //TODO:play with it
            nSlots = 0;

            y = workArea.Y + spacing;
            height = (workArea.Height - (lines + 1) * spacing) / lines;
            for (i = 0; i < lines; i++)
            {
                n = Math.Min(process_holders.Count - nSlots,
                             (int)Math.Ceiling((double)process_holders.Count / lines));

                x = workArea.X + spacing;
                width = (workArea.Width - (n + 1) * spacing) / n;

                for (j = 0; j < n; j++)
                {
                    ProcessHolder ph = process_holders[nSlots];
                    ph.rect = new Rectangle(x, y, width, height);
                    process_holders[nSlots] = ph;
                    x += width + spacing;
                    nSlots++;
                }

                y += height + spacing;
            }
        }

        private void openRePoseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            showRepose();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        ConfigForm configForm = null;

        private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            configForm = new ConfigForm();
            configForm.ShowDialog();
            configForm = null;
        }

        private void notifyIcon_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                showRepose();
            }
        }
    }
}
